package astoliarskyi.lab5;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CircularBuffer {
	
	private final int[] buffer;
	private final Lock lock = new ReentrantLock();
	private final Condition producers = lock.newCondition();
	private final Condition consumers = lock.newCondition();
	private int dataAmount = 0;
	private int startPointer = 0;
	private int endPointer = 0;
	
	public CircularBuffer(int dimension){
		if (dimension == 0) dimension = 1;
		buffer = new int[dimension];
	}
	
	public static void main(String[] args){
		CircularBuffer buffer = new CircularBuffer(5);
		List<Runnable> tasks = new LinkedList<Runnable>();
		tasks.add(new ConsumerTask("FirstCosumer", buffer, 5));
		tasks.add(new ConsumerTask("SecondConsumer", buffer, 3));
		tasks.add(new ProducerTask("FirstProducer", buffer, 10, 100));
		tasks.add(new ProducerTask("SecondProducer", buffer, 12, 25));
		for (Runnable task : tasks)
			new Thread(task).start();
	}
	
	public void put(int objectToPut){
		lock.lock();
		try{
			while(dataAmount == buffer.length){
				try {
					producers.await();
				} catch (InterruptedException e) { /* It won't be interrupted. */ }
			}
			buffer[endPointer] = objectToPut;
			if (endPointer + 1 == buffer.length)
				endPointer = 0;
			else
				endPointer = endPointer + 1;
			dataAmount = dataAmount + 1;
			consumers.signal();
		} finally {
			lock.unlock();
		}
	}
	
	public int get(){
		lock.lock();
		try{
			while(dataAmount == 0){
				try{
					consumers.await();
				} catch (InterruptedException e) { /* It won't be interrupted. */ }
			}
			int objectToReturn = buffer[startPointer];
			if (startPointer + 1 == buffer.length)
				startPointer = 0;
			else
				startPointer = startPointer + 1;
			dataAmount = dataAmount - 1;
			producers.signal();
			return objectToReturn;
		} finally {
			lock.unlock();
		}
	}
}

class ProducerTask implements Runnable{

	private final String producerName;
	private final CircularBuffer buffer;
	private final int power;
	private final int upTo;

	public ProducerTask(String producerName, CircularBuffer circularBuffer, int power, int upTo){
		this.producerName = producerName;
		buffer = circularBuffer;
		this.power = power;
		this.upTo = upTo;
	}

	@Override
	public void run() {
		int objectsLeft = power;
		Random random = new Random();
		while (objectsLeft > 0){
			int randomNumber = random.nextInt(upTo);
			System.out.println(producerName + " produced " + randomNumber);
			buffer.put(randomNumber);
			System.out.println(producerName + " put " + randomNumber + " to buffer");
			objectsLeft--;
		}
	}
}

class ConsumerTask implements Runnable{

	private final String consumerName;
	private final CircularBuffer buffer;
	private final int power;

	public ConsumerTask(String consumerName, CircularBuffer circularBuffer, int power){
		this.consumerName = consumerName;
		this.buffer = circularBuffer;
		this.power = power;
	}

	@Override
	public void run() {
		int objectsLeft = power;
		while (objectsLeft > 0){
			int takenObject = buffer.get();
			System.out.println(consumerName + " consumed " + takenObject);
			objectsLeft--;
		}

	}

}
